package cn.yh.register.order.service.impl;

import cn.yh.register.client.HospitalFeignClient;
import cn.yh.register.client.UserFeignClient;
import cn.yh.register.common.constant.MqConst;
import cn.yh.register.common.exception.RegisterException;
import cn.yh.register.common.helper.HttpRequestHelper;
import cn.yh.register.common.result.ResultCodeEnum;
import cn.yh.register.common.service.RabbitService;
import cn.yh.register.enums.OrderStatusEnum;
import cn.yh.register.model.order.OrderInfo;
import cn.yh.register.model.user.Patient;
import cn.yh.register.order.mapper.OrderInfoMapper;
import cn.yh.register.order.service.OrderService;
import cn.yh.register.order.service.WeiXinService;
import cn.yh.register.vo.hosp.ScheduleOrderVo;
import cn.yh.register.vo.msm.MsmVo;
import cn.yh.register.vo.order.*;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;

@Slf4j
@Transactional(rollbackFor = Exception.class)
@Service
public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderService {

    @Autowired
    HospitalFeignClient hospitalFeignClient;
    @Autowired
    UserFeignClient userFeignClient;
    @Autowired
    RabbitService rabbitService;
    @Autowired
    WeiXinService weiXinService;
    /**
     * 保存订单
     * @param scheduleId 排班id
     * @param patientId  就诊人id
     * @return 订单id
     */
    @Override
    public Long saveOrder(String scheduleId, Long patientId) {
        // 获取预约下单的排班数据
        ScheduleOrderVo scheduleOrderVo = hospitalFeignClient.getScheduleOrderVo(scheduleId);
        if (scheduleOrderVo == null) {
            throw new RegisterException(ResultCodeEnum.PARAM_ERROR);
        }
        // 调用户服务- 获取就诊人信息
        Patient patient = userFeignClient.getPatientOrder(patientId);
        if (patient == null) {
            throw new RegisterException(ResultCodeEnum.PARAM_ERROR);
        }
        // 判断是否可预约
        DateTime startTime = new DateTime(scheduleOrderVo.getStartTime());
        DateTime endTime = new DateTime(scheduleOrderVo.getEndTime());
        if (startTime.isAfterNow() || endTime.isBeforeNow()) {
            throw new RegisterException(ResultCodeEnum.TIME_NO);
        }
        // 没号就不可挂
        if(scheduleOrderVo.getAvailableNumber() <= 0) {
            throw new RegisterException(ResultCodeEnum.NUMBER_NO);
        }

        // 订单数据插入到数据库
        OrderInfo orderInfo = new OrderInfo();
        BeanUtils.copyProperties(scheduleOrderVo,orderInfo);
        // 订单号
        String outTradeNo = System.currentTimeMillis() + ""+ new Random().nextInt(100);
        orderInfo.setOutTradeNo(outTradeNo);
        // 设置医院的排班主键
        orderInfo.setScheduleId(scheduleOrderVo.getHosScheduleId());
        orderInfo.setUserId(patient.getUserId());
        orderInfo.setPatientId(patientId);
        orderInfo.setPatientName(patient.getName());
        orderInfo.setPatientPhone(patient.getPhone());
        // 订单状态 0：预约成功，待支付
        orderInfo.setOrderStatus(OrderStatusEnum.UNPAID.getStatus());
        // 保存订单
        this.save(orderInfo);
        // 调医院服务-获取医院的签名信息
        SignInfoVo signInfoVo = hospitalFeignClient.getSignInfoVo(scheduleOrderVo.getHoscode());
        if (signInfoVo == null) {
            throw new RegisterException(ResultCodeEnum.PARAM_ERROR);
        }
        // 对接医院系统下订单
        Map<String, Object> paramMap = new HashMap<>();
        //排班信息
        paramMap.put("hoscode",orderInfo.getHoscode());
        paramMap.put("depcode",orderInfo.getDepcode());
        paramMap.put("hosScheduleId",orderInfo.getScheduleId());
        paramMap.put("reserveDate",new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd"));
        paramMap.put("reserveTime", orderInfo.getReserveTime());
        paramMap.put("amount",orderInfo.getAmount());
        // 就诊人信息
        paramMap.put("name", patient.getName());
        paramMap.put("certificatesType",patient.getCertificatesType());
        paramMap.put("certificatesNo", patient.getCertificatesNo());
        paramMap.put("sex",patient.getSex());
        paramMap.put("birthdate", patient.getBirthdate());
        paramMap.put("phone",patient.getPhone());
        paramMap.put("isMarry", patient.getIsMarry());
        paramMap.put("provinceCode",patient.getProvinceCode());
        paramMap.put("cityCode", patient.getCityCode());
        paramMap.put("districtCode",patient.getDistrictCode());
        paramMap.put("address",patient.getAddress());
        // 联系人信息
        paramMap.put("contactsName",patient.getContactsName());
        paramMap.put("contactsCertificatesType", patient.getContactsCertificatesType());
        paramMap.put("contactsCertificatesNo",patient.getContactsCertificatesNo());
        paramMap.put("contactsPhone",patient.getContactsPhone());
        paramMap.put("timestamp", HttpRequestHelper.getTimestamp());
        // 签名
        paramMap.put("sign", HttpRequestHelper.getSign(paramMap, signInfoVo.getSignKey()));
        // 调医院的接口挂号
        JSONObject jsonObject = HttpRequestHelper.sendRequest(paramMap, signInfoVo.getApiUrl() + "/order/submitOrder");
        if (200 == jsonObject.getInteger("code")) {
            // 预约成功，更新订单信息
            JSONObject data = jsonObject.getJSONObject("data");
            // 医院预约记录的唯一标识
            String hosRecordId = data.getString("hosRecordId");
            // 预约序号
            Integer number = data.getInteger("number");
            // 取号时间
            String fetchTime = data.getString("fetchTime");
            // 取号地址
            String fetchAddress = data.getString("fetchAddress");
            // 更新订单数据
            orderInfo.setHosRecordId(hosRecordId);
            orderInfo.setNumber(number);
            orderInfo.setFetchTime(fetchTime);
            orderInfo.setFetchAddress(fetchAddress);
            this.updateById(orderInfo);
            // 可预约数
            Integer reservedNumber = data.getInteger("reservedNumber");
            // 剩余预约数
            Integer availableNumber = data.getInteger("availableNumber");
            // TODO 发送Mq消息
            // 封装消息实体
            OrderMqVo orderMqVo= new OrderMqVo();
            orderMqVo.setReservedNumber(reservedNumber);
            orderMqVo.setAvailableNumber(availableNumber);
            orderMqVo.setScheduleId(scheduleId);
            MsmVo msmVo = new MsmVo();
            msmVo.setPhone(patient.getPhone());
//            msmVo.setTemplateCode("");//模板code
            String reserveDate =
                    new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd")
                            + (orderInfo.getReserveTime()==0 ? "上午": "下午");
            Map<String,Object> param = new HashMap<String,Object>(){{
                put("title", orderInfo.getHosname()+"|"+orderInfo.getDepname()+"|"+orderInfo.getTitle());
                put("amount", orderInfo.getAmount());
                put("reserveDate", reserveDate);
                put("name", orderInfo.getPatientName());
                put("quitTime", new DateTime(orderInfo.getQuitTime()).toString("yyyy-MM-dd HH:mm"));
            }};
            msmVo.setParam(param);//模板参数（短信内容）
            orderMqVo.setMsmVo(msmVo);
            //发送短信  由医院服务消费
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_ORDER,MqConst.ROUTING_ORDER,orderMqVo);
            log.info("订单服务发送更新号源信息：{}",orderMqVo);
        } else {
            throw new RegisterException(jsonObject.getString("message"),
                    ResultCodeEnum.FAIL.getCode());
        }

        return orderInfo.getId();
    }

    /**
     * 订单分页查询
     * @param page    分页参数
     * @param queryVo 查询条件
     * @return 分页数据
     */
    @Override
    public IPage<OrderInfo> selectPage(Page<OrderInfo> page, OrderQueryVo queryVo) {
        // 构造条件
        LambdaQueryWrapper<OrderInfo> wrapper=new LambdaQueryWrapper<>();
        wrapper.eq(!StringUtils.isEmpty(queryVo.getUserId()),OrderInfo::getUserId,queryVo.getUserId())
                .eq(!StringUtils.isEmpty(queryVo.getPatientId()), OrderInfo::getPatientId, queryVo.getPatientId())
                .like(!StringUtils.isEmpty(queryVo.getPatientName()), OrderInfo::getPatientName, queryVo.getPatientName())
                // 医院名称
                .like(!StringUtils.isEmpty(queryVo.getKeyword()), OrderInfo::getHosname, queryVo.getKeyword())
                // 订单号
                .like(!StringUtils.isEmpty(queryVo.getOutTradeNo()), OrderInfo::getOutTradeNo, queryVo.getOutTradeNo())
                .eq(!StringUtils.isEmpty(queryVo.getOrderStatus()), OrderInfo::getOrderStatus, queryVo.getOrderStatus())
                .eq(!StringUtils.isEmpty(queryVo.getReserveDate()), OrderInfo::getReserveDate, queryVo.getReserveDate())
                .ge(!StringUtils.isEmpty(queryVo.getCreateTimeBegin()), OrderInfo::getCreateTime, queryVo.getCreateTimeBegin())
                .le(!StringUtils.isEmpty(queryVo.getCreateTimeEnd()), OrderInfo::getCreateTime, queryVo.getCreateTimeEnd());

        Page<OrderInfo> orderInfoPage = baseMapper.selectPage(page, wrapper);
        orderInfoPage.getRecords().forEach(this::packOrderInfo);
        return orderInfoPage;
    }

    /**
     * 获取订单详情
     * @param id 订单id
     * @return 订单详情
     */
    @Override
    public OrderInfo getOrderInfo(Long id) {
        OrderInfo orderInfo = baseMapper.selectById(id);

        this.packOrderInfo(orderInfo);
        return orderInfo;
    }

    /**
     * 查询订单详情
     *
     * @param orderId 订单id
     * @return 订单信息 && 就诊人信息
     */
    @Override
    public Map<String, Object> showOrderInfo(Long orderId) {
        //订单信息
        OrderInfo orderInfo = baseMapper.selectById(orderId);
        this.packOrderInfo(orderInfo);
        //就诊人信息
        Patient patient = userFeignClient.getPatientOrder(orderInfo.getPatientId());
        Map<String,Object> map=new HashMap<>();
        map.put("orderInfo", orderInfo);
        map.put("patient", patient);
        return map;
    }

    /**
     * 取消订单
     * @param orderId 订单 id
     * @return 是否成功
     */
    @Override
    public Boolean cancelOrder(Long orderId) {
        // 先查订单信息
        OrderInfo orderInfo = this.getOrderInfo(orderId);
        // 先判断当前时间能否取消订单
        if (new DateTime(orderInfo.getQuitTime()).isBeforeNow()) {
            throw new RegisterException(ResultCodeEnum.CANCEL_ORDER_NO);
        }
        // 对接医院系统取消预约
        SignInfoVo signInfoVo = hospitalFeignClient.getSignInfoVo(orderInfo.getHoscode());
        if (signInfoVo==null) {
            throw new RegisterException(ResultCodeEnum.PARAM_ERROR);
        }
        // 组装数据
        Map<String,Object> paramMap = new HashMap<>();
        paramMap.put("hoscode", orderInfo.getHoscode());
        paramMap.put("hosRecordId", orderInfo.getHosRecordId());
        paramMap.put("timestamp",HttpRequestHelper.getTimestamp());
        paramMap.put("sign",HttpRequestHelper.getSign(paramMap,signInfoVo.getSignKey()));
        // 请求医院系统
        JSONObject jsonObject = HttpRequestHelper.sendRequest(paramMap, signInfoVo.getApiUrl() + "/order/updateCancelStatus");
        if (jsonObject.getInteger("code")==200) {
            // 医院取消成功
            // 判断订单状态是否已支付
            if (orderInfo.getOrderStatus().equals(OrderStatusEnum.PAID.getStatus())) {
                // 已支付 调微信退款
                Boolean flag = weiXinService.refund(orderId);
                if(!flag){
                    throw new RegisterException(ResultCodeEnum.CANCEL_ORDER_FAIL);
                }
            }
            // 更新订单状态
            orderInfo.setOrderStatus(OrderStatusEnum.CANCLE.getStatus());
            this.updateById(orderInfo);
            // 更新号源 & 发送短信通知  消息接收方将剩余预约数+1
            OrderMqVo orderMqVo = new OrderMqVo();
            orderMqVo.setScheduleId(orderInfo.getScheduleId());
            MsmVo msmVo = new MsmVo();
            msmVo.setPhone(orderInfo.getPatientPhone());
            // msmVo.setTemplateCode("");
            String reserveDate = new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd") + (orderInfo.getReserveTime()==0 ? "上午": "下午");
            Map<String,Object> param = new HashMap<String,Object>(){{
                put("title", orderInfo.getHosname()+"|"+orderInfo.getDepname()+"|"+orderInfo.getTitle());
                put("reserveDate", reserveDate);
                put("name", orderInfo.getPatientName());
            }};
            msmVo.setParam(param);
            orderMqVo.setMsmVo(msmVo);
            // 发送到MQ
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_ORDER,MqConst.ROUTING_ORDER,orderMqVo);
            log.info("订单服务发送更新号源信息：{}",orderMqVo);
        }else {
            throw new RegisterException(jsonObject.getString("message"),ResultCodeEnum.FAIL.getCode());
        }
        return true;
    }

    /**
     * 就诊提醒
     */
    @Override
    public void patientTips() {
        // 查询当天的订单 条件就诊日期
        List<OrderInfo> orderInfoList = baseMapper.selectList(new LambdaQueryWrapper<OrderInfo>()
                .eq(OrderInfo::getReserveDate, new DateTime().toString("yyyy-MM-dd")));
        if (orderInfoList != null) {
            for (OrderInfo orderInfo : orderInfoList) {
                String patientPhone = orderInfo.getPatientPhone();
                MsmVo msmVo = new MsmVo();
                msmVo.setPhone(patientPhone);
//                msmVo.setTemplateCode("");
                String reserveDate = new DateTime(orderInfo.getReserveDate()).toString("yyyy-MM-dd") + (orderInfo.getReserveTime()==0 ? "上午": "下午");
                Map<String,Object> param = new HashMap<String,Object>(){{
                    put("title", orderInfo.getHosname()+"|"+orderInfo.getDepname()+"|"+orderInfo.getTitle());
                    put("reserveDate", reserveDate);
                    put("name", orderInfo.getPatientName());
                }};
                msmVo.setParam(param);
                // 发送到MQ
                rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_MSM,MqConst.ROUTING_MSM_ITEM,msmVo);
            }
        }

    }

    /**
     * 统计订单量
     * @param queryVo 查询条件
     * @return 订单量
     */
    @Override
    public Map<String, Object> getCountMap(OrderCountQueryVo queryVo) {
        List<OrderCountVo> countVoList = baseMapper.selectOrderCount(queryVo);
        // 日期列表
        List<String> reserveDateList = countVoList.stream().map(OrderCountVo::getReserveDate).collect(Collectors.toList());
        // 数量列表
        List<Integer> countList = countVoList.stream().map(OrderCountVo::getCount).collect(Collectors.toList());
        Map<String,Object> map = new HashMap<>();
        map.put("dateList",reserveDateList);
        map.put("countList",countList);
        return map;
    }

    // 处理数字 转为汉字
    private void packOrderInfo(OrderInfo orderInfo){
        orderInfo.getParam().put("orderStatusString",
                OrderStatusEnum.getStatusNameByStatus(orderInfo.getOrderStatus()));
    }


}
